/*!
 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import i18n from 'nfvo-utils/i18n/i18n.js';
import classNames from 'classnames';
import Select from 'nfvo-components/input/SelectInput.jsx';
import Overlay from 'react-bootstrap/lib/Overlay.js';
import Tooltip from 'react-bootstrap/lib/Tooltip.js';

export const other = { OTHER: 'Other' };

class InputOptions extends React.Component {
    static propTypes = {
        values: PropTypes.arrayOf(
            PropTypes.shape({
                enum: PropTypes.string,
                title: PropTypes.string
            })
        ),
        isEnabledOther: PropTypes.bool,
        label: PropTypes.string,
        selectedValue: PropTypes.string,
        multiSelectedEnum: PropTypes.oneOfType([
            PropTypes.string,
            PropTypes.array
        ]),
        selectedEnum: PropTypes.string,
        otherValue: PropTypes.string,
        overlayPos: PropTypes.string,
        onEnumChange: PropTypes.func,
        onOtherChange: PropTypes.func,
        onBlur: PropTypes.func,
        isRequired: PropTypes.bool,
        isMultiSelect: PropTypes.bool,
        isValid: PropTypes.bool,
        disabled: PropTypes.bool
    };

    state = {
        otherInputDisabled: !this.props.otherValue
    };

    oldProps = {
        selectedEnum: '',
        otherValue: '',
        multiSelectedEnum: []
    };

    render() {
        let {
            label,
            isRequired,
            values,
            otherValue,
            onOtherChange,
            isMultiSelect,
            onBlur,
            multiSelectedEnum,
            selectedEnum,
            isValid,
            children,
            isReadOnlyMode
        } = this.props;
        const dataTestId = this.props['data-test-id']
            ? { 'data-test-id': this.props['data-test-id'] }
            : {};
        let currentMultiSelectedEnum = [];
        let currentSelectedEnum = '';
        let otherInputDisabled =
            (isMultiSelect &&
                (multiSelectedEnum === undefined ||
                    multiSelectedEnum.length === 0 ||
                    multiSelectedEnum[0] !== other.OTHER)) ||
            (!isMultiSelect &&
                (selectedEnum === undefined || selectedEnum !== other.OTHER));
        if (isMultiSelect) {
            currentMultiSelectedEnum = multiSelectedEnum;
            if (!otherInputDisabled) {
                currentSelectedEnum = multiSelectedEnum
                    ? multiSelectedEnum.toString()
                    : undefined;
            }
        } else if (selectedEnum) {
            currentSelectedEnum = selectedEnum;
        }
        if (!onBlur) {
            onBlur = () => {};
        }

        return (
            <div className="validation-input-wrapper">
                <div
                    className={classNames('form-group', {
                        required: isRequired,
                        'has-error': !isValid
                    })}>
                    {label && <label className="control-label">{label}</label>}
                    {isMultiSelect && otherInputDisabled ? (
                        <Select
                            {...dataTestId}
                            ref={input => (this.input = input)}
                            value={currentMultiSelectedEnum}
                            className="options-input"
                            clearable={false}
                            required={isRequired}
                            disabled={
                                isReadOnlyMode || Boolean(this.props.disabled)
                            }
                            onBlur={() => onBlur()}
                            onMultiSelectChanged={value =>
                                this.multiSelectEnumChanged(value)
                            }
                            options={this.renderMultiSelectOptions(values)}
                            multi
                        />
                    ) : (
                        <div
                            className={classNames('input-options', {
                                'has-error': !isValid
                            })}>
                            <select
                                {...dataTestId}
                                ref={input => (this.input = input)}
                                label={label}
                                className="form-control input-options-select"
                                value={currentSelectedEnum}
                                style={{
                                    width: otherInputDisabled ? '100%' : '100px'
                                }}
                                onBlur={() => onBlur()}
                                disabled={
                                    isReadOnlyMode ||
                                    Boolean(this.props.disabled)
                                }
                                onChange={value => this.enumChanged(value)}
                                type="select">
                                {children ||
                                    (values &&
                                        values.length &&
                                        values.map((val, index) =>
                                            this.renderOptions(val, index)
                                        ))}
                                {onOtherChange && (
                                    <option key="other" value={other.OTHER}>
                                        {i18n(other.OTHER)}
                                    </option>
                                )}
                            </select>

                            {!otherInputDisabled && (
                                <div className="input-options-separator" />
                            )}
                            <input
                                className="form-control input-options-other"
                                placeholder={i18n('other')}
                                ref={otherValue =>
                                    (this.otherValue = otherValue)
                                }
                                style={{
                                    display: otherInputDisabled
                                        ? 'none'
                                        : 'block'
                                }}
                                disabled={
                                    isReadOnlyMode ||
                                    Boolean(this.props.disabled)
                                }
                                value={otherValue || ''}
                                onBlur={() => onBlur()}
                                onChange={() => this.changedOtherInput()}
                            />
                        </div>
                    )}
                </div>
                {this.renderErrorOverlay()}
            </div>
        );
    }

    renderOptions(val, index) {
        return (
            <option key={index} value={val.enum}>
                {val.title}
            </option>
        );
    }

    renderMultiSelectOptions(values) {
        let { onOtherChange } = this.props;
        let optionsList = [];
        if (onOtherChange) {
            optionsList = values
                .map(option => {
                    return {
                        label: option.title,
                        value: option.enum
                    };
                })
                .concat([
                    {
                        label: i18n(other.OTHER),
                        value: i18n(other.OTHER)
                    }
                ]);
        } else {
            optionsList = values.map(option => {
                return {
                    label: option.title,
                    value: option.enum
                };
            });
        }
        if (optionsList.length > 0 && optionsList[0].value === '') {
            optionsList.shift();
        }
        return optionsList;
    }

    renderErrorOverlay() {
        let position = 'right';
        const { errorText = '', isValid = true, type, overlayPos } = this.props;

        if (overlayPos) {
            position = overlayPos;
        } else if (
            type === 'text' ||
            type === 'email' ||
            type === 'number' ||
            type === 'password'
        ) {
            position = 'bottom';
        }

        return (
            <Overlay
                show={!isValid}
                placement={position}
                target={() => {
                    let { otherInputDisabled } = this.state;
                    let target = otherInputDisabled
                        ? ReactDOM.findDOMNode(this.input)
                        : ReactDOM.findDOMNode(this.otherValue);
                    return target.offsetParent ? target : undefined;
                }}
                container={this}>
                <Tooltip
                    id={`error-${errorText.replace(' ', '-')}`}
                    className="validation-error-message">
                    {errorText}
                </Tooltip>
            </Overlay>
        );
    }

    getValue() {
        let res = '';
        let { isMultiSelect } = this.props;
        let { otherInputDisabled } = this.state;

        if (otherInputDisabled) {
            res = isMultiSelect ? this.input.getValue() : this.input.value;
        } else {
            res = this.otherValue.value;
        }
        return res;
    }

    enumChanged() {
        let enumValue = this.input.value;
        let {
            onEnumChange,
            onOtherChange,
            isMultiSelect,
            onChange
        } = this.props;
        this.setState({
            otherInputDisabled:
                !Boolean(onOtherChange) || enumValue !== other.OTHER
        });

        let value = isMultiSelect ? [enumValue] : enumValue;
        if (onEnumChange) {
            onEnumChange(value);
        }
        if (onChange) {
            onChange(value);
        }
    }

    multiSelectEnumChanged(enumValue) {
        let { onEnumChange, onOtherChange } = this.props;
        let selectedValues = enumValue.map(enumVal => {
            return enumVal.value;
        });

        if (this.state.otherInputDisabled === false) {
            selectedValues.shift();
        } else if (selectedValues.includes(i18n(other.OTHER))) {
            selectedValues = [i18n(other.OTHER)];
        }

        this.setState({
            otherInputDisabled:
                !Boolean(onOtherChange) ||
                !selectedValues.includes(i18n(other.OTHER))
        });
        onEnumChange(selectedValues);
    }

    changedOtherInput() {
        let { onOtherChange } = this.props;
        onOtherChange(this.otherValue.value);
    }

    componentDidUpdate() {
        let {
            otherValue,
            selectedEnum,
            onInputChange,
            multiSelectedEnum
        } = this.props;
        if (
            this.oldProps.otherValue !== otherValue ||
            this.oldProps.selectedEnum !== selectedEnum ||
            this.oldProps.multiSelectedEnum !== multiSelectedEnum
        ) {
            this.oldProps = {
                otherValue,
                selectedEnum,
                multiSelectedEnum
            };
            onInputChange();
        }
    }

    static getTitleByName(values, name) {
        for (let key of Object.keys(values)) {
            let option = values[key].find(option => option.enum === name);
            if (option) {
                return option.title;
            }
        }
        return name;
    }
}

export default InputOptions;
